package com.doudou.pay.helper.controller;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.doudou.pay.helper.commons.IdWorker;
import com.doudou.pay.helper.commons.JsonUtil;
import com.doudou.pay.helper.commons.PayConstans;
import com.doudou.pay.helper.commons.QRCodeUtil;
import com.doudou.pay.helper.commons.StreamUtils;
import com.doudou.pay.helper.entities.Config;
import com.doudou.pay.helper.entities.IntraAccounts;
import com.doudou.pay.helper.entities.MerchantOrderQuery;
import com.doudou.pay.helper.entities.MerchantRequestPay;
import com.doudou.pay.helper.entities.PayAccounts;
import com.doudou.pay.helper.entities.PayUser;
import com.doudou.pay.helper.entities.RechargeAccounts;
import com.doudou.pay.helper.entities.ResponsePay;
import com.doudou.pay.helper.entities.User;
import com.doudou.pay.helper.entities.UserPay;
import com.doudou.pay.helper.mapper.ConfigMapper;
import com.doudou.pay.helper.mapper.IntraAccountsMapper;
import com.doudou.pay.helper.mapper.UserMapper;
import com.doudou.pay.helper.server.protoco.ProtocoConsts;
import com.doudou.pay.helper.service.MessageCacheService;
import com.doudou.pay.helper.service.PayService;
import com.doudou.pay.helper.service.PayUserService;
import com.doudou.pay.helper.websocket.WebSocketResponse;

@RestController
public class ThirdPartyPayController {

	@Autowired
	private PayService payService;

	@Autowired
	private PayUserService payUserService;

	@Autowired
	private MessageCacheService messageCacheService;

	@Autowired
	private IdWorker idWorker;

	@Autowired
	private ConfigMapper configMapper;

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private IntraAccountsMapper intraAccountsMapper;

	@RequestMapping("/thirdParty/qrcode")
	public MerchantRequestPay merchantRequestPay(UserPay userPay) {
		MerchantRequestPay requestPay = new MerchantRequestPay(userPay);

		List<PayUser> payUsers = payUserService.getPayUserByUserId(userPay
				.getUserId());
		if (null == payUsers || payUsers.size() == 0) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("不存在的用户");
			return requestPay;
		}

		// 判断在线客户端数量
		Map<String, Integer> onlineClientMap = payUserService
				.getOnlineAuthToken(userPay.getUserId());
		if (onlineClientMap.size() == 0) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("客户端不在线");
			return requestPay;
		}

		if (StringUtils.isEmpty(userPay.getType())) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("请选择支付方式");
			return requestPay;
		}

		if (StringUtils.isEmpty(userPay.getThirdPartyOrderId())) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("请输入订单编号");
			return requestPay;
		}

		if (userPay.getMoney() <= 0) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("请输入金额");
			return requestPay;
		}

		MerchantOrderQuery query = new MerchantOrderQuery();
		query.setUserId(userPay.getUserId());
		query.setThirdPartyOrderId(userPay.getRemark());
		MerchantOrderQuery order = payService.getThirdPartyOrderQuery(query);
		if (null != order) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("订单已存在");
			return requestPay;
		}

		userPay.setSessionKey(UUID.randomUUID().toString());
		WebSocketResponse response = payService.pay(userPay, onlineClientMap);
		if (response.getCode() != ProtocoConsts.SUCCESS) {
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine("获取二维码失败");
			return requestPay;
		}

		// 夯住请求
		long startTime = System.currentTimeMillis();
		ResponsePay responsePay = null;
		while ((responsePay = messageCacheService.getResponsePay(userPay
				.getSessionKey())) == null
				&& System.currentTimeMillis() - startTime < 1000 * 20) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if (null == responsePay) {
			requestPay.setCode(0);
			requestPay.setDefine("获取二维码超时");
			return requestPay;
		}

		if (ProtocoConsts.SUCCESS == responsePay.getCode()) {
			requestPay.setCode(ProtocoConsts.SUCCESS);
			requestPay.setDefine(ProtocoConsts.SUCCESS_DEFINE);
			requestPay.setPayUrl(responsePay.getPayUrl());
			return requestPay;
		}

		requestPay.setCode(ProtocoConsts.FAIL);
		requestPay.setDefine(ProtocoConsts.FAIL_DEFINE);
		return requestPay;
	}

	@ResponseBody
	@RequestMapping(value = "/thirdParty/pay", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE
			+ ";Charset=UTF-8")
	public MerchantRequestPay merchantRequestPay(HttpServletRequest request,
			HttpServletResponse response) {

		MerchantRequestPay requestPay = new MerchantRequestPay();
		try {
			ServletInputStream inputStream = request.getInputStream();
			String postData = StreamUtils.copyToString(inputStream,
					Charset.forName("utf-8"));

			System.out.println("pay:" + postData);
			UserPay userPay = JsonUtil.parseObject(postData, UserPay.class);
			requestPay = new MerchantRequestPay(userPay);
			if (StringUtils.isEmpty(userPay.getType())) {
				requestPay.setCode(ProtocoConsts.NO_PAY_TYPE);
				requestPay.setDefine("请选择支付方式");
				return requestPay;
			}

			if (StringUtils.isEmpty(userPay.getRemark())) {
				requestPay.setCode(ProtocoConsts.NO_ORDER_NUM);
				requestPay.setDefine("请输入订单编号");
				return requestPay;
			}

			if (userPay.getMoney() <= 0) {
				requestPay.setCode(ProtocoConsts.MONEY_LIMIT);
				requestPay.setDefine("请输入金额");
				return requestPay;
			}

			if (!payService.checkMoney(userPay.getMoney())) {
				requestPay.setCode(ProtocoConsts.MONEY_OVER_LIMIT);
				requestPay.setDefine("金额超出限制");
				return requestPay;
			}

			User user = userMapper.findByUsername(userPay.getUserId());
			if (user == null) {
				requestPay.setCode(ProtocoConsts.USER_ERROR);
				requestPay.setDefine("用户无效");
				return requestPay;
			}

			if (user.getStatus() != 1) {
				requestPay.setCode(ProtocoConsts.USER_ERROR);
				requestPay.setDefine("用户无效");
				return requestPay;
			}

			BigDecimal b = new BigDecimal(userPay.getMoney());
			double df = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
			userPay.setMoney(df);

			MerchantOrderQuery query = new MerchantOrderQuery();
			query.setUserId(userPay.getUserId());
			query.setThirdPartyOrderId(userPay.getRemark());
			MerchantOrderQuery order = payService
					.getThirdPartyOrderQuery(query);
			if (null != order) {

				if (userPay.getMoney() != order.getMoney()) {
					requestPay.setCode(ProtocoConsts.ORDER_EXIST);
					requestPay.setDefine("订单已存在");
					return requestPay;
				}

				if ("3".equals(order.getStatus())) {
					requestPay.setCode(ProtocoConsts.ORDER_HAVE_PAY);
					requestPay.setDefine("订单已支付");
					return requestPay;
				}

				if ("6".equals(order.getStatus())) {
					requestPay.setCode(ProtocoConsts.ORDER_OVER_TIME);
					requestPay.setDefine("订单已超时");
					return requestPay;
				}

				requestPay.setAccountId(order.getFundsAccount());
				requestPay
						.setPayUrl(QRCodeUtil.createQRCode(order.getQrcode()));
				requestPay.setCode(ProtocoConsts.SUCCESS);
				requestPay.setName(order.getFundsAccountName());
				requestPay.setThirdPartyOrderId(userPay.getRemark());

				return requestPay;
			}

			PayAccounts payAccounts = payService.getAccounts(
					userPay.getUserId(), userPay.getType(), userPay.getMoney(),
					user);
			if (payAccounts == null) {
				requestPay.setCode(ProtocoConsts.ACCOUNTS_PAY_LIMIT);
				requestPay.setDefine("资源不足");
				return requestPay;
			}
			userPay.setFundsAccountName(payAccounts.getName());
			userPay.setFundsAccount(payAccounts.getAccountName());

			userPay.setPayId(idWorker.nextId() + "");
			String qrUrl = getQrUrl(userPay, payAccounts);
			userPay.setQrcode(qrUrl);
			userPay.setSessionKey(UUID.randomUUID().toString());
			WebSocketResponse rsp = payService.pay(userPay, user);
			if (rsp.getCode() != ProtocoConsts.SUCCESS) {
				requestPay.setCode(ProtocoConsts.NO_MONEY);
				requestPay.setDefine("余额不足,请续费");
				return requestPay;
			}

			requestPay.setAccountId(userPay.getFundsAccount());
			requestPay.setPayUrl(QRCodeUtil.createQRCode(userPay.getQrcode()));
			requestPay.setCode(ProtocoConsts.SUCCESS);
			requestPay.setName(payAccounts.getName());
			requestPay.setThirdPartyOrderId(userPay.getRemark());

		} catch (Exception e) {
			System.out.println(e.getMessage());
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine(ProtocoConsts.FAIL_DEFINE);
			return requestPay;
		}

		return requestPay;
	}

	@ResponseBody
	@RequestMapping(value = "/thirdParty/new/pay", method = RequestMethod.POST)
	public MerchantRequestPay newMerchantRequestPay(UserPay userPay) {

		MerchantRequestPay requestPay = new MerchantRequestPay();
		try {
			requestPay = new MerchantRequestPay(userPay);
			if (StringUtils.isEmpty(userPay.getType())) {
				requestPay.setCode(ProtocoConsts.NO_PAY_TYPE);
				requestPay.setDefine("请选择支付方式");
				return requestPay;
			}

			if (StringUtils.isEmpty(userPay.getRemark())) {
				requestPay.setCode(ProtocoConsts.NO_ORDER_NUM);
				requestPay.setDefine("请输入订单编号");
				return requestPay;
			}

			if (userPay.getMoney() <= 0) {
				requestPay.setCode(ProtocoConsts.MONEY_LIMIT);
				requestPay.setDefine("请输入金额");
				return requestPay;
			}

			if (!payService.checkMoney(userPay.getMoney())) {
				requestPay.setCode(ProtocoConsts.MONEY_OVER_LIMIT);
				requestPay.setDefine("金额超出限制");
				return requestPay;
			}

			User user = userMapper.findByUsername(userPay.getUserId());
			if (user == null) {
				requestPay.setCode(ProtocoConsts.USER_ERROR);
				requestPay.setDefine("用户无效");
				return requestPay;
			}

			if (user.getStatus() != 1) {
				requestPay.setCode(ProtocoConsts.USER_ERROR);
				requestPay.setDefine("用户无效");
				return requestPay;
			}

			BigDecimal b = new BigDecimal(userPay.getMoney());
			double df = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
			userPay.setMoney(df);

			MerchantOrderQuery query = new MerchantOrderQuery();
			query.setUserId(userPay.getUserId());
			query.setThirdPartyOrderId(userPay.getRemark());
			MerchantOrderQuery order = payService
					.getThirdPartyOrderQuery(query);
			if (null != order) {
				if (userPay.getMoney() != order.getMoney()) {
					requestPay.setCode(ProtocoConsts.ORDER_EXIST);
					requestPay.setDefine("订单已存在");
					return requestPay;
				}

				if ("3".equals(order.getStatus())) {
					requestPay.setCode(ProtocoConsts.ORDER_HAVE_PAY);
					requestPay.setDefine("订单已支付");
					return requestPay;
				}

				if ("6".equals(order.getStatus())) {
					requestPay.setCode(ProtocoConsts.ORDER_OVER_TIME);
					requestPay.setDefine("订单已超时");
					return requestPay;
				}

				requestPay.setAccountId(order.getFundsAccount());
				requestPay
						.setPayUrl(QRCodeUtil.createQRCode(order.getQrcode()));
				requestPay.setCode(ProtocoConsts.SUCCESS);
				requestPay.setName(order.getFundsAccountName());
				requestPay.setThirdPartyOrderId(userPay.getRemark());
				return requestPay;
			}

			PayAccounts payAccounts = payService.getAccounts(
					userPay.getUserId(), userPay.getType(), userPay.getMoney(),
					user);
			if (payAccounts == null) {
				requestPay.setCode(ProtocoConsts.ACCOUNTS_PAY_LIMIT);
				requestPay.setDefine("资源不足");
				return requestPay;
			}
			userPay.setFundsAccountName(payAccounts.getName());
			userPay.setFundsAccount(payAccounts.getAccountName());
			userPay.setPayId(idWorker.nextId() + "");
			String qrUrl = getQrUrl(userPay, payAccounts);

			userPay.setQrcode(qrUrl);
			userPay.setSessionKey(UUID.randomUUID().toString());
			WebSocketResponse rsp = payService.pay(userPay, user);
			if (rsp.getCode() != ProtocoConsts.SUCCESS) {
				requestPay.setCode(ProtocoConsts.NO_MONEY);
				requestPay.setDefine("余额不足,请续费");
				return requestPay;
			}

			requestPay.setAccountId(userPay.getFundsAccount());
			requestPay.setPayUrl(QRCodeUtil.createQRCode(userPay.getQrcode()));
			requestPay.setCode(ProtocoConsts.SUCCESS);
			requestPay.setName(payAccounts.getName());
			requestPay.setThirdPartyOrderId(userPay.getRemark());
		} catch (Exception e) {
			System.out.println(e.getMessage());
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine(ProtocoConsts.FAIL_DEFINE);
			return requestPay;
		}

		return requestPay;
	}

	@ResponseBody
	@RequestMapping(value = "/thirdParty/recharge", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE
			+ ";Charset=UTF-8")
	public MerchantRequestPay merchantRequestRecharge(
			HttpServletRequest request, HttpServletResponse response) {

		MerchantRequestPay requestPay = new MerchantRequestPay();
		try {
			ServletInputStream inputStream = request.getInputStream();
			String postData = StreamUtils.copyToString(inputStream,
					Charset.forName("utf-8"));

			System.out.println("recharge:" + postData);
			UserPay userPay = JsonUtil.parseObject(postData, UserPay.class);
			requestPay = new MerchantRequestPay(userPay);
			if (StringUtils.isEmpty(userPay.getType())) {
				requestPay.setCode(ProtocoConsts.FAIL);
				requestPay.setDefine("请选择支付方式");
				return requestPay;
			}

			userPay.setRemark(idWorker.nextId() + "");
			if (userPay.getMoney() < 10) {
				requestPay.setCode(ProtocoConsts.FAIL);
				requestPay.setDefine("充值金额不得低于10元");
				return requestPay;
			}

			BigDecimal b = new BigDecimal(userPay.getMoney());
			double df = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
			userPay.setMoney(df);

			RechargeAccounts accounts = payService.getRechargeAccounts(
					userPay.getType(), userPay.getMoney());
			if (accounts == null) {
				requestPay.setCode(ProtocoConsts.ACCOUNTS_PAY_LIMIT);
				requestPay.setDefine("资源不足");
				return requestPay;
			}
			userPay.setFundsAccountName(accounts.getName());
			userPay.setFundsAccount(accounts.getAccountName());

			userPay.setPayId(idWorker.nextId() + "");
			String qrUrl = "alipays://platformapi/startapp?appId=09999988&actionType=toAccount&goBack=NO&amount="
					+ userPay.getMoney()
					+ "&userId="
					+ userPay.getFundsAccount() + "&memo=" + userPay.getPayId();
			userPay.setQrcode(qrUrl);
			userPay.setPayType(1);
			userPay.setSessionKey(UUID.randomUUID().toString());
			WebSocketResponse rsp = payService.recharge(userPay);
			if (rsp.getCode() != ProtocoConsts.SUCCESS) {
				requestPay.setCode(ProtocoConsts.FAIL);
				requestPay.setDefine("获取二维码失败");
				return requestPay;
			}

			requestPay.setAccountId(userPay.getFundsAccount());
			requestPay.setPayUrl(QRCodeUtil.createQRCode(userPay.getQrcode()));
			requestPay.setThirdPartyOrderId(userPay.getRemark());
			requestPay.setCode(ProtocoConsts.SUCCESS);
			requestPay.setName(accounts.getName());
		} catch (Exception e) {
			System.out.println(e.getMessage());
			requestPay.setCode(ProtocoConsts.FAIL);
			requestPay.setDefine(ProtocoConsts.FAIL_DEFINE);
			return requestPay;
		}

		return requestPay;
	}

	@RequestMapping("/thirdParty/query")
	public MerchantOrderQuery merchantRequestPay(HttpServletRequest request)
			throws UnsupportedEncodingException {

		String userId = new String(request.getParameter("userId").getBytes(
				"ISO-8859-1"), "UTF-8");
		String thirdPartyOrderId = new String(request.getParameter(
				"thirdPartyOrderId").getBytes("ISO-8859-1"), "UTF-8");

		MerchantOrderQuery query = new MerchantOrderQuery();
		query.setUserId(userId);
		query.setThirdPartyOrderId(thirdPartyOrderId);

		if (StringUtils.isEmpty(userId)) {
			query.setCode(ProtocoConsts.QUERY_NO_USERID);
			query.setDefine("商户Id为空");
			return query;
		}

		if (StringUtils.isEmpty(thirdPartyOrderId)) {
			query.setCode(ProtocoConsts.QUERY_NO_ORDER_NUM);
			query.setDefine("订单信息为空");
			return query;
		}

		MerchantOrderQuery order = payService.getThirdPartyOrderQuery(query);
		if (null == order) {
			query.setCode(ProtocoConsts.QUERY_ORDER_NOT_EXIST);
			query.setDefine("查询失败");
			return query;
		}

		order.setCode(ProtocoConsts.SUCCESS);
		order.setDefine(ProtocoConsts.SUCCESS_DEFINE);
		return order;
	}

	private String getQrUrl(UserPay userPay, PayAccounts payAccounts)
			throws AlipayApiException {

		if (payAccounts.getPayAccountType() == 0) {
			String qrUrl = "alipays://platformapi/startapp?appId=09999988&actionType=toAccount&goBack=NO&amount="
					+ userPay.getMoney()
					+ "&userId="
					+ userPay.getFundsAccount() + "&memo=" + userPay.getPayId();
			return qrUrl;
		} else {

			String order_no = userPay.getPayId();
			String body = "";

			String total_fee = String.valueOf(userPay.getMoney()); // 真实金钱

			AlipayClient alipayClient = new DefaultAlipayClient(
					"https://openapi.alipay.com/gateway.do",
					payAccounts.getAppid(), payAccounts.getPrivateKey(),
					"json", "UTF-8", payAccounts.getPublicKey(), "RSA2"); // 获得初始化的AlipayClient
			AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();// 创建API对应的request类
			request.setBizContent("{" + "    \"out_trade_no\":\"" + order_no
					+ "\"," + "    \"total_amount\":\"" + total_fee + "\","
					+ "    \"body\":\"" + body + "\","
					+ "    \"subject\":\"扫码支付\","
					+ "    \"timeout_express\":\"90m\"}");// 设置业务参数

			Config config = configMapper.findByKey("alipaynotify");
			request.setNotifyUrl(config.getValue());
			AlipayTradePrecreateResponse response = alipayClient
					.execute(request);// 通过alipayClient调用API，获得对应的response类
			System.err.print(response.getBody());
			// 根据response中的结果继续业务逻辑处理
			if (response.getMsg().equals("Success")) {
				String qrcode = response.getQrCode();
				System.out.println(qrcode);
				return qrcode;
			}
		}

		return null;
	}

	@RequestMapping("/alipaynotify")
	public String notify(HttpServletRequest request) throws AlipayApiException {
		// 一定要验签，防止黑客篡改参数
		Map<String, String[]> parameterMap = request.getParameterMap();
		parameterMap.forEach((key, value) -> System.out.println(key + "="
				+ value[0]));

		// https://docs.open.alipay.com/54/106370
		// 获取支付宝POST过来反馈信息
		Map<String, String> params = new HashMap<>();
		Map requestParams = request.getParameterMap();
		for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i]
						: valueStr + values[i] + ",";
			}
			params.put(name, valueStr);
		}

		String appId = params.get("app_id");
		String outTradeNo = params.get("out_trade_no");
		String tradeNo = params.get("trade_no");

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("appid", appId);
		IntraAccounts intraAccounts = intraAccountsMapper.findAccounts(map);
		if (intraAccounts == null) {
			return "success";
		}

		// boolean flag = AlipaySignature.rsaCheckV1(params,
		// intraAccounts.getPublicKey(), "UTF-8", "RSA2");
		// if (flag) {

		/**
		 * TODO 需要严格按照如下描述校验通知数据的正确性
		 *
		 * 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
		 * 并判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
		 * 同时需要校验通知中的seller_id（或者seller_email)
		 * 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
		 *
		 * 上述有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
		 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
		 * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
		 */
		payService.updatePayInfo(PayConstans.PAY_STATUS_SUCCESS, outTradeNo,
				tradeNo, "成功", intraAccounts);
		return "success";
		// }
		//
		// return null;
	}

}
